/**
 *
 * jns 简单通信协议 javasript 实现
 *
 * 负责直接对二进制数据 打包/解包
 */

const typePing = 0x0;
const typePong = 0x1;
const typeReq = 0x2;
const typeRes = 0x3;


class JNSProtocol{

    constructor(){
        this.headDataLen = 4;
        // wait 等待新的包
        // half 等待后续数据
        this.status = 'wait';
        this.dataBuffer = new Buffer(0);

        this.errorHandler = function (msg, stack, more ) {
            console.log("JNSProtocal Error ! ", msg, stack, more);
        }

    }

    clearAllBuff(){
        let oldStatus = this.status;
        this.status = 'wait';
        let oldHeadBufer = this.dataBuffer;
        this.dataBuffer = new Buffer(0);
        return [oldStatus, oldHeadBufer]
    }

    readHead(data){
        return data.readInt32BE();
    }

    saveBuffer(data){
        if( data && data.length ){
            this.dataBuffer = Buffer.concat([this.dataBuffer , data]);
        }
    }

    plusBuffer(data){
        if( !data ){
            data = new Buffer(0);
        }
        if( this.dataBuffer.length > 0 ){
            let ret = Buffer.concat([this.dataBuffer , data]);
            this.dataBuffer = new Buffer(0);
            return ret;
        }else{
            return data;
        }
    }

    check(data, length){
        if( !length ){
            length = data.length;
        }
        let check = 0;
        for( let i = 0 ; i < length ; i++){
            check ^= data[i];
        }
        return 0 == check;
    }

    checkHead(data){
        return this.check(data, this.headDataLen + 1)
    }

    get pingPack(){
        return this.pack(undefined, typePing)
    }

    get pongPack(){
        return this.pack(undefined, typePong)
    }

    pack(data, type){
        let b2;

        if( ! data ){
            b2 = Buffer.alloc(0);
        }else {
            b2 = new Buffer(data);
        }

        if( undefined == type ){
            type = typeReq;
        }


        let id = Buffer.from( Math.random().toString(36).substr(2, 8) );
        let len = b2.length + 11;

        let b1 = Buffer.allocUnsafe(4);
        b1.writeInt32BE(len)
        let c1 = 0;
        for ( let i = 0 ; i< this.headDataLen ; i++ ){
            c1 = c1 ^ b1[i];
        }
        let c2 = 0;
        for ( let i = 0 ; i < 8 ; i++ ){
            c2 = c2 ^ id[i];
        }
        c2 = c2 ^ type

        for ( let i = 0 ; i < b2.length ; i++ ){
            c2 = c2 ^ b2[i];
        }

        let b3 = Buffer.concat([
            b1,
            Buffer.from([c1, c2]),
            Buffer.from([type]),
            id,
            b2]);

        return {
            data : b3,
            type : type,
            id : id
        }


        return false;
    }


    /**
     * 重新开始
     * @param data
     */
    waitNewFrameHandler(data){

        data = this.plusBuffer(data);
        if( data.length < 5 ){
            this.saveBuffer(data);
            return;
        }

        if( !this.checkHead(data) ){
            this.error("headError", this.clearAllBuff())
            this.status = 'wait';
            return;
        }

        this.saveBuffer(data);
        this.status = 'half';
        this.waitNewDataHandler();
    }

    waitNewDataHandler(data){
        data = this.plusBuffer(data);
        let head = this.readHead(data);
        if( data.length < head + this.headDataLen ){
            // 数据长度不够 继续等待数据
            this.saveBuffer(data);
            return;
        }

        let use = data.slice( 0, head + this.headDataLen )
        let unuse = data.slice( head + this.headDataLen )
        this.saveBuffer(unuse);
        if ( this.check(use) ){
            this.data(use)
        }else{
            //this.saveErrorData(data);
            this.error("dataError", this.clearAllBuff(), use);
        }
        this.status = 'wait'
    }

    statusError(data){
        // 清空所有数据 尝试恢复正常
        this.error("statusError", this.clearAllBuff())

    }

    hook(data){
        switch (this.status){
            case 'wait':
                this.waitNewFrameHandler(data);
                break;
            case 'half':
                this.waitNewDataHandler(data);
                break;
            default :
                // 错误的状态
                this.statusError(this.status);
                break;
        }
    }


    setErrorHandler(cb){
        this.errorHandler = cb;
    }

    setResponseHandler(cb){
        this.responseHandler = cb;
    }
    setRequestHandler(cb){
        this.requestHandler = cb;
    }
    setPingHandler(cb){
        this.pingHandler = cb;
    }


    data(d){
        let id = d.slice(this.headDataLen + 3, this.headDataLen + 11).toString();

        switch (d[ this.headDataLen + 2 ]){
            case typeReq :
                this.requestHandler && this.requestHandler( id, d.slice(this.headDataLen + 11));
                break;
            case typeRes :
                this.responseHandler && this.responseHandler( id, d.slice(this.headDataLen + 11));
                break;
            case typePing :
                this.pingHandler && this.pingHandler();
                break;
            case typePong :

            default :
                break;
        }

    }

    /**
     *
     * @param type statusError dataError headError
     * @param more
     */
    error(type, data, more){
        this.errorHandler && this.errorHandler(type, data, more);
    }
}


JNSProtocol.typePing = typePing;
JNSProtocol.typePong = typePong;
JNSProtocol.typeReq = typeReq;
JNSProtocol.typeRes = typeRes;

module.exports = JNSProtocol;